grammar Arduino;

options {
	output = AST;
	language = Java;
	backtrack = true;
}

tokens {
	FUNC_CALL;
	FUNC_DECL;
	FUNC_PARAMS;
	ARRAY_ACCESS;
	BLOCK;
	POST_INC;
	POST_DEC;
	PRE_INC;
	PRE_DEC;
	DIM;
	DIM_PREFIX;
	DIM_SUFFIX;
	DIM_SUFFIX_WITH_EMPTY_ARRAY;
	IF;
	WHILE;
	FOR;
	FOR_INIT;
	FOR_COND;
	FOR_INC;
	CAST;
}

@header {
	package net.grapsus.arduino;
}

@lexer::header {
	package net.grapsus.arduino;
}

program : statement*;

/* declarations */
type_specifier
	: 'void'
	| 'char'
	| 'short'
	| 'int'
	| 'long'
	| 'float'
	| 'double'
	| 'signed'
	| 'unsigned'
	;

type_qualifier
	: 'const'
	| 'volatile'
	;

declarator_prefix
	: type_qualifier* type_specifier -> ^(DIM_PREFIX type_specifier type_qualifier*)
	; 

declarator_suffix
	:   ('[' constant_expression ']')* ('[' ']') -> ^(DIM_SUFFIX_WITH_EMPTY_ARRAY ^(constant_expression)*)
	|   ('[' constant_expression ']')* -> ^(DIM_SUFFIX ^(constant_expression)*)
	;

/* expressions */
argument_expression_list
	:   assignment_expression (','! assignment_expression)*
	;

additive_expression
	: (multiplicative_expression) ('+'^ multiplicative_expression | '-'^ multiplicative_expression)*
	;

multiplicative_expression
	: (cast_expression) ('*'^ cast_expression | '/'^ cast_expression | '%'^ cast_expression)*
	;

cast_expression
	: '(' type_specifier ')' cast_expression -> ^(CAST type_specifier cast_expression)
	| unary_expression
	;

unary_expression
	: postfix_expression
	| '++' unary_expression -> ^(PRE_INC unary_expression)
	| '--' unary_expression -> ^(PRE_DEC unary_expression)
	| unary_operator^ cast_expression
	| 'sizeof' unary_expression
	| 'sizeof' '(' type_specifier ')'
	;

postfix_expression
	: primary_expression ('[' expression ']')+ -> ^(ARRAY_ACCESS ^(primary_expression expression+))
        | primary_expression '(' argument_expression_list ')' -> ^(FUNC_CALL ^(primary_expression argument_expression_list))
        | primary_expression '(' ')' -> ^(FUNC_CALL ^(primary_expression))
        | primary_expression '++' -> ^(POST_INC ^(primary_expression))
        | primary_expression '--' -> ^(POST_DEC ^(primary_expression))
        | primary_expression
	;

unary_operator
	: '+'
	| '-'
	| '~'
	| '!'
	;


primary_expression
	: ID
	| constant
	| '('! expression ')'!
	;

constant
    :   INT
    |   FLOAT
    |   STRING
    ;

expression
	: assignment_expression (','! assignment_expression)*
	;

constant_expression
	: conditional_expression
	;

assignment_expression
	: conditional_expression
	| lvalue assignment_operator^ assignment_expression
	;
	
lvalue
	: declarator_prefix ID declarator_suffix? -> ^(DIM declarator_prefix ID declarator_suffix?)
	| ID
	;

function_declaration
	: name=lvalue '(' (p1=lvalue)? (',' p2=lvalue)* ')' compound_statement -> ^(FUNC_DECL $name ^(FUNC_PARAMS $p1* $p2*) compound_statement)
	;

assignment_operator
	: '='
	| '*='
	| '/='
	| '%='
	| '+='
	| '-='
	| '<<='
	| '>>='
	| '&='
	| '^='
	| '|='
	;

conditional_expression
	: logical_or_expression ('?' expression ':' conditional_expression)?
	;

logical_or_expression
	: logical_and_expression ('||'^ logical_and_expression)*
	;

logical_and_expression
	: inclusive_or_expression ('&&'^ inclusive_or_expression)*
	;

inclusive_or_expression
	: exclusive_or_expression ('|'^ exclusive_or_expression)*
	;

exclusive_or_expression
	: and_expression ('^'^ and_expression)*
	;

and_expression
	: equality_expression ('&'^ equality_expression)*
	;
equality_expression
	: relational_expression (('=='^|'!='^) relational_expression)*
	;

relational_expression
	: shift_expression (('<'^|'>'^|'<='^|'>='^) shift_expression)*
	;

shift_expression
	: additive_expression (('<<'^|'>>'^) additive_expression)*
	;

/* statements */
statement
	: labeled_statement
	| function_declaration
	| compound_statement
	| expression_statement
	| selection_statement
	| iteration_statement
	| jump_statement
	;

labeled_statement
	: ID ':' statement
	| 'case' constant_expression ':' statement
	| 'default' ':' statement
	;

compound_statement
	: '{' statement_list? '}' -> ^(BLOCK  statement_list?)
	;

statement_list
	: statement+
	;

expression_statement
	: ';'!
	| expression ';'!
	;

selection_statement
	: 'if' '(' expression ')' s1=statement ('else' s2=statement)? -> ^(IF expression $s1 $s2*)
	| 'switch'^ '('! expression ')'! statement
	;

iteration_statement
	: 'while' '(' expression ')' statement -> ^(WHILE expression statement)
	| 'do' statement 'while' '(' expression ')' ';'!
	| 'for' '(' i=expression_statement c=expression_statement l=expression? ')' statement -> ^(FOR ^(FOR_INIT $i) ^(FOR_COND $c) ^(FOR_INC $l) statement)
	;

jump_statement
	: 'goto' ID ';'!
	| 'continue' ';'!
	| 'break' ';'!
	| 'return' ';'!
	| 'return' expression ';'!
	;

/* misc */

ID  :	('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
    ;

INT :	'0'..'9'+
    ;

FLOAT
    :   ('0'..'9')+ '.' ('0'..'9')* EXPONENT?
    |   '.' ('0'..'9')+ EXPONENT?
    |   ('0'..'9')+ EXPONENT
    ;

COMMENT
    :   '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    |   '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
    ;

WS  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) {$channel=HIDDEN;}
    ;

STRING
    :  '"' ( ESC_SEQ | ~('\\'|'"') )* '"'
    ;

CHAR:  '\'' ( ESC_SEQ | ~('\''|'\\') ) '\''
    ;

fragment
EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;

fragment
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;

fragment
ESC_SEQ
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   OCTAL_ESC
    ;

fragment
OCTAL_ESC
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;
